package com.yehun.config;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.yehun.utils.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Objects;
import java.util.concurrent.Callable;

/**
 * spring redis cache实现
 */
public class SpringRedisCache implements Cache {

    private static final Logger logger = LoggerFactory.getLogger(SpringRedisCache.class);

    private RedisTemplate<String, Object> redisTemplate;
    private String name;
    /**
     * 过期时间 单位:秒
     */
    private long expired;

    private String getKey(Object key) {
        String finalKey;
        if (key instanceof String) {
            finalKey = (String) key;
        } else {
            finalKey = key.toString();
        }
        return finalKey;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        return this.redisTemplate;
    }

    @Override
    public ValueWrapper get(Object key) {
        if (null != key) {
            String stringKey = this.getKey(key);
            Object object = redisTemplate.execute((RedisCallback<Object>) connection -> {
                byte[] bytesKey = stringKey.getBytes();
                byte[] bytesValue = connection.get(bytesKey);
                if (bytesValue == null) {
                    return null;
                }
                logger.info("Cacheable命中缓存 => " + stringKey);
                return JsonUtils.toObject(bytesValue);
            });
            if (null != object) {
                return new SimpleValueWrapper(object);
            }
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T get(Object key, Class<T> type) {
        if (null != key || null == type) {
            String stringKey = this.getKey(key);
            return redisTemplate.execute((RedisCallback<T>) connection -> {
                byte[] bytesKey = stringKey.getBytes();
                byte[] bytesValue = connection.get(bytesKey);
                if (null == bytesValue) {
                    return null;
                }
                logger.info("Cacheable命中缓存 => " + stringKey + " | T:" + type.getName());
                return (T) JsonUtils.toObject(bytesValue);
            });
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T get(Object key, Callable<T> callable) {
        if (null != key) {
            String stringKey = this.getKey(key);
            return redisTemplate.execute((RedisCallback<T>) connection -> {
                byte[] bytesKey = stringKey.getBytes();
                byte[] bytesValue = connection.get(bytesKey);
                if (null == bytesValue) {
                    return null;
                }
                return (T) JsonUtils.toObject(bytesValue);
            });
        }
        return null;
    }

    @Override
    public void put(Object key, Object value) {
        if (null != key && null != value) {
            String stringKey = this.getKey(key);
            if (stringKey != null && !Objects.equals(stringKey, "")) {
                logger.info("Cacheable添加缓存 => " + stringKey);
                redisTemplate.execute((RedisCallback<Boolean>) connection -> {
                    SerializerFeature[] serializerFeatures = {
                            SerializerFeature.WriteClassName,
                            SerializerFeature.WriteDateUseDateFormat, //格式化日期
                            SerializerFeature.WriteMapNullValue, //空值字段输出
                            SerializerFeature.WriteNullStringAsEmpty, //字符串为null时输出""
                            SerializerFeature.WriteNullListAsEmpty, //列表为null时输出[]
                            SerializerFeature.WriteNullBooleanAsFalse, //boolean类型为null时输出false
                            SerializerFeature.WriteNullNumberAsZero //整型为null时输出0
                    };
                    byte[] bytes = JsonUtils.toBytes(value, serializerFeatures);
                    connection.set(stringKey.getBytes(), bytes);
                    // 设置超时间
                    if (expired != 0) {
                        connection.expire(stringKey.getBytes(), expired);
                    }
                    return true;
                });
            }
        }
    }

    @Override
    public ValueWrapper putIfAbsent(Object o, Object o1) {
        return null;
    }

    @Override
    public void evict(Object key) {
        if (null != key) {
            final String finalKey = this.getKey(key);
            if (finalKey != null && !Objects.equals(finalKey, "")) {
                redisTemplate.execute((RedisCallback<Long>) connection -> connection.del(finalKey.getBytes()));
            }
        }
    }

    @Override
    public void clear() {
        redisTemplate.execute((RedisCallback<String>) connection -> {
            connection.flushDb();
            return "ok";
        });
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setExpired(long expired) {
        this.expired = expired;
    }
}
